﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using ReplaceCsv.ConvertMethods;
using System.Collections;
using Microsoft.VisualBasic.FileIO;
using System.Windows.Forms;
using System.Globalization;
using ReplaceCsv.WallLogParser;

namespace ReplaceCsv
{
    class WallConvert1 : InputOutputConverter
    {
        string[] _inputFileNames;
        protected string[] inputFileNames
        {
            set { this._inputFileNames = value; }
            get { return this._inputFileNames; }
        }
        string _outputFileName;
        protected string outputFileName
        {
            set { this._outputFileName = value; }
            get { return this._outputFileName; }
        }
        string _output2FileName;
        protected string output2FileName
        {
            set { this._output2FileName = value; }
            get { return this._output2FileName; }
        }
        string _output3FileName;
        protected string output3FileName
        {
            set { this._output3FileName = value; }
            get { return this._output3FileName; }
        }

        private WallLogAnalyzeLogic wal;
        private WallLogOntimeOutput log1;
        private WallLogAvgOutput log2;
        private WallLogRankCountOutput log3;

        //#ジエスタ#
        private GiestaLogExtractOutput log4;

        private List<ConvertColumn> columnConvert = new List<ConvertColumn>();
        public WallConvert1() {}
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="inputFileName"></param>
        /// <param name="outputFileNames"></param>
        private WallConvert1(string[] input, string[] output, Dictionary<string, object> option)
        {
            //inputとoutputは読み書き可能なことを前提とする。
            this.inputFileNames = input;
            this._outputFileName = output[0];
            this._output2FileName = output[1];
            this._output3FileName = output[2];
            init(input, output, option);
        }
        /// <summary>
        /// ログ解析インスタンスとファイル名を結びつける
        /// </summary>
        private void init(string[] input, string[] output, Dictionary<string, object> option)
        {
            //リミットの初期値 3分
            double limitTimeSecond = 3 * 60;
            if (option.ContainsKey("limitTimeSecond")) double.TryParse((string)option["limitTimeSecond"], (NumberStyles.Float | NumberStyles.AllowThousands) , null , out limitTimeSecond);
            //step1ベース解析クラスの生成
            wal = new WallLogAnalyzeLogic(limitTimeSecond);
            setTimeCategory();
            wal.addCategoryInfomationDef("～3分以上", limitTimeSecond);

            //#ジエスタ# #20130620
            setJiestaParsePattern();

            //step2滞在時間出力クラスとファイルをマッピング
            this.log1 = new WallLogOntimeOutput(output[0]);
            //step3平均滞在時間出力クラスとファイルをマッピング
            this.log2 = new WallLogAvgOutput(output[1]);
            //step4ランキング出力クラスとファイルをマッピング
            this.log3 = new WallLogRankCountOutput(output[2]);

            //#ジエスタ# step5 ジエスタ対応
            this.log4 = new GiestaLogExtractOutput(output[3]);
        }
        private void setTimeCategory()
        {
            //wal.addCategoryInfomationDef("～15秒",0d,15d);
            //wal.addCategoryInfomationDef("～30秒", 15d, 30d);
            //wal.addCategoryInfomationDef("～45秒", 30d, 45d);
            //wal.addCategoryInfomationDef("～1分", 45d, 60d);
            //wal.addCategoryInfomationDef("～1分15秒", 60d, 75d);
            //wal.addCategoryInfomationDef("～1分30秒", 75d, 90d);
            //wal.addCategoryInfomationDef("～1分45秒", 90d, 105d);
            //wal.addCategoryInfomationDef("～2分", 105d, 120d);
            //wal.addCategoryInfomationDef("～2分15秒", 120d, 135d);
            //wal.addCategoryInfomationDef("～2分30秒", 135d, 150d);
            //wal.addCategoryInfomationDef("～2分45秒", 135d, 165d);
            //wal.addCategoryInfomationDef("～3分", 165d, 180d);
            //wal.addCategoryInfomationDef("～3分15秒", 180d, 195d);
            //wal.addCategoryInfomationDef("～3分30秒", 195d, 210d);
            //wal.addCategoryInfomationDef("～3分45秒", 210d, 225d);
            //wal.addCategoryInfomationDef("～4分", 225d, 240d);
            //wal.addCategoryInfomationDef("～4分15秒", 240d, 255d);
            //wal.addCategoryInfomationDef("～4分30秒", 255d, 270d);
            //wal.addCategoryInfomationDef("～4分45秒", 270d, 285d);
            //wal.addCategoryInfomationDef("～5分", 285, 300d);
            //20130324 修正 1秒～4秒対応
            //wal.addCategoryInfomationDef("～5秒", 0d, 5d);
            wal.addCategoryInfomationDef("～5秒", 1d, 5d);
            wal.addCategoryInfomationDef("～10秒", 5d, 10d);
            wal.addCategoryInfomationDef("～15秒", 10d, 15d);
            wal.addCategoryInfomationDef("～20秒", 15d, 20d);
            wal.addCategoryInfomationDef("～25秒", 20d, 25d);
            wal.addCategoryInfomationDef("～30秒", 25d, 30d);
            wal.addCategoryInfomationDef("～35秒", 30d, 35d);
            wal.addCategoryInfomationDef("～40秒", 35d, 40d);
            wal.addCategoryInfomationDef("～45秒", 40d, 45d);
            wal.addCategoryInfomationDef("～50秒", 45d, 50d);
            wal.addCategoryInfomationDef("～55秒", 50d, 55d);
            wal.addCategoryInfomationDef("～1分", 55d, 60d);
            wal.addCategoryInfomationDef("～1分5秒", 60d, 65d);
            wal.addCategoryInfomationDef("～1分10秒", 65d, 70d);
            wal.addCategoryInfomationDef("～1分15秒", 70d, 75d);
            wal.addCategoryInfomationDef("～1分20秒", 75d, 80d);
            wal.addCategoryInfomationDef("～1分25秒", 80d, 85d);
            wal.addCategoryInfomationDef("～1分30秒", 85d, 90d);
            wal.addCategoryInfomationDef("～1分35秒", 90d, 95d);
            wal.addCategoryInfomationDef("～1分40秒", 95d, 100d);
            wal.addCategoryInfomationDef("～1分45秒", 100d, 105d);
            wal.addCategoryInfomationDef("～1分50秒", 105d, 110d);
            wal.addCategoryInfomationDef("～1分55秒", 110d, 115d);
            wal.addCategoryInfomationDef("～2分", 115d, 120d);
            wal.addCategoryInfomationDef("～2分5秒", 120d, 125d);
            wal.addCategoryInfomationDef("～2分10秒", 125d, 130d);
            wal.addCategoryInfomationDef("～2分15秒", 130d, 135d);
            wal.addCategoryInfomationDef("～2分20秒", 135d, 140d);
            wal.addCategoryInfomationDef("～2分25秒", 140d, 145d);
            wal.addCategoryInfomationDef("～2分30秒", 145d, 150d);
            wal.addCategoryInfomationDef("～2分35秒", 150d, 155d);
            wal.addCategoryInfomationDef("～2分40秒", 155d, 160d);
            wal.addCategoryInfomationDef("～2分45秒", 160d, 165d);
            wal.addCategoryInfomationDef("～2分50秒", 165d, 170d);
            wal.addCategoryInfomationDef("～2分55秒", 170d, 175d);
            wal.addCategoryInfomationDef("～3分", 175d, 180d);
        }
        //後でファイル化すること！！
        private void setJiestaParsePattern()
        {
            //test用
            //testSetJiestaParsePattern();
            // JSON による設定追加対応　20120624
            GiestaExtractParameterJSONList list 
                = GiestaParsePattern.getParameterList();
            foreach (GiestaExtractParameterJSONBean bean in list.JSONList)
            {
                wal.setGiestaParser(new GiestaParser(bean.category,bean.matchPattern,bean.matchGroup,bean.delList,bean.convertRegList));
            }
        }
        private void testSetJiestaParsePattern()
        {
            #region | エントリーシステム |
            Dictionary<int,int> cap = new Dictionary<int,int>();
            cap[1] = 1;
            List<string> dellist = new List<string>();
            dellist.Add("\\+カード");
            dellist.Add("\\+ケータイ");
            dellist.Add("動画");
            Dictionary<string,string> convlist = new Dictionary<string,string>();
            wal.setGiestaParser(new GiestaParser("エントリーシステム", "^(.*?)\\[at エントリーシステムって\\？\\]", cap, dellist, convlist));
            
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            convlist.Add("^btnTouch$","タッチキー");
            convlist.Add("^btnCazas$","CAZAS+");
            convlist.Add("^btnSystem$","システムキー");
            //wal.setJiestaParser(new JiestaParser("エントリーシステム", "(エントリーシステム)選択\\:(btnCazas|btnSystem|btnTouch)", cap, dellist, convlist));
            wal.setGiestaParser(new GiestaParser("エントリーシステム", "エントリーシステム選択\\:(.+?):追加", cap, dellist, convlist));
            #endregion | エントリーシステム |

            #region | オーナメント対応ドア |
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            convlist.Add("^karakusa$","唐草");
            convlist.Add("^bouquet$", "ブーケ");
            convlist.Add("^brooch$","ブローチ");
            convlist.Add("^mickey1$","ミッキー1");
            convlist.Add("^mickey2$","ミッキー2");
            convlist.Add("^byou$","鋲");

            wal.setGiestaParser(new GiestaParser("オーナメント対応ドア", "オーナメント選択\\:(.+)\\:追加", cap, dellist, convlist));
            #endregion | オーナメント対応ドア |

            #region | スタイルから選ぶ |
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            convlist.Add("^Modern$","モダン");
            convlist.Add("^Riche$","リッシュ");
            convlist.Add("^Naturel$", "ナチュレル");

            wal.setGiestaParser(new GiestaParser("スタイルから選ぶ", "^(.*?)\\[at スタイル選択\\]", cap, dellist, convlist));
            #endregion | スタイルから選ぶ |

            #region | ドアカラー |
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            cap[2] = 2;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            wal.setGiestaParser(new GiestaParser("ドアカラー", "^\\[D\\](.+)\\:(.+)\\[at", cap, dellist, convlist));

            
            cap = new Dictionary<int, int>();
            //色選択はキャプチャーが逆
            cap[1] = 2;
            cap[2] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            wal.setGiestaParser(new GiestaParser("ドアカラー", "^\\[C\\]色選択\\:(.+)\\:\\[at 商品詳細\\:(.+)\\]", cap, dellist, convlist));
            #endregion | ドアカラー |

            #region | トップ |
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            wal.setGiestaParser(new GiestaParser("トップ", "^(デザインから選ぶ)\\[at TOP\\]", cap, dellist, convlist));

            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            wal.setGiestaParser(new GiestaParser("トップ", "^(こだわりから選ぶ)\\[at TOP\\]", cap, dellist, convlist));
            #endregion | トップ |

            #region | 採風、採光 |
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            wal.setGiestaParser(new GiestaParser("採風、採光", "^(採風＋採光|採光|採光なし)\\[at", cap, dellist, convlist));
            #endregion | 採風、採光 |

            #region | 断熱性能 |
            cap = new Dictionary<int, int>();
            cap[1] = 1;
            dellist = new List<string>();
            convlist = new Dictionary<string, string>();
            wal.setGiestaParser(new GiestaParser("断熱性能", "^断熱性能(K.+?)\\[at", cap, dellist, convlist));
            #endregion | 断熱性能 |
        }

        /// <summary>
        /// インスタンスを生成して返却する
        /// </summary>
        /// <param name="inputFileName">入力ファイル名（あらかじめ検査済であること）</param>
        /// <param name="outputFilename">出力ファイル名（あらかじめ検査済であること）</param>
        /// <returns></returns>
        public  InputOutputConverter createInstance(string inputFileName, string outputFileName)
        {
            ///このメソッドは現在使用しない。。
            return null;
        }
        /// <summary>
        /// インスタンスを生成して返却する
        /// </summary>
        /// <param name="inputFileName">入力ファイル名（あらかじめ検査済であること）</param>
        /// <param name="outputFilename">出力ファイル名配列（あらかじめ検査済であること）</param>
        /// <returns></returns>
        public  InputOutputConverter createInstance(string[] inputFileName, string[] outputFileNames,Dictionary<string,object> option)
        {
            InputOutputConverter result = new WallConvert1(inputFileName, outputFileNames,option);
            return result;
        }

        public bool execute(ProgressBar bar)
        {
            return fileConvert(bar);
        }
        public  bool execute()
        {
            return fileConvert(null);
        }
        /// <summary>
        /// 置換処理メインメソッド
        /// </summary>
        /// <param name="bar"></param>
        /// <returns></returns>
        private bool fileConvert(ProgressBar bar)
        {
            //////////////
            //WallLogAnalyzeLogicクラス
            //getWallName (ファイル名からwall名を取得するメソッド）
            //AddRowメソッド(１行のrowデータを追加していく）
            //AnalyzeOnTimeFromDatas(全部読み終わったら解析）

            //////
            bool result = false;

            //TextFieldParser parser = new TextFieldParser(this.inputFileNames,Encoding.GetEncoding(932));
            //TextFieldParser checkParser = new TextFieldParser(this.inputFileNames,Encoding.GetEncoding(932));
            //パーサーを初期化する。
            foreach (string inputFileName in this.inputFileNames)
            {
                TextFieldParser parser = new TextFieldParser(inputFileName, Encoding.GetEncoding(932));
                TextFieldParser checkParser = null;
                InitializeParser(parser, checkParser);

                //ファイル名を取得
                string name = Path.GetFileName(inputFileName);
                //wall名を取得
                string wall = this.wal.getWallName(name);

                //using (FileStream u = File.OpenWrite(this.outputFileName))
                //{
                    //CP932
                    ////TextWriter outCsv = new StreamWriter(u, Encoding.GetEncoding(932));

                    ///////initBar(bar, checkParser);
                    while (!parser.EndOfData)
                    {
                        string[] rows = parser.ReadFields(); // 1行読み込み
                        ////outCsv.WriteLine(this.convert(rows));
                        ////doBar();

                        //変換クラス解析ロジックへ
                        this.wal.AddRow(rows, wall, name);
                    }
                    //一旦パーサーをクローズ
                    parser.Close();

                    //バーの処理は後で行う
                    //closeBar();
                    ////outCsv.Flush();
                    //result = true;
                //}
            }
            //解析を実行
            this.wal.AnalyzeOnTimeFromDatas();
            //ログをそれぞれ出力
#if ONLY_GIESTA
            //#ジエスタ#
            //step4 ジエスタログを出力
            this.log4.output(this.wal.dataList);
#else
            //step滞在時間を出力
            this.log1.output(this.wal.dataList);
            //step2平均滞在時間を出力
            this.log2.output(this.wal.dataList);
            //step3ランキング用CSVを出力
            this.log3.output(this.wal.dataList);

            //#ジエスタ#
            //step4 ジエスタログを出力
            this.log4.output(this.wal.dataList);
#endif


            //書き込み用の時間を取得する
            DateTime dt = DateTime.Now;

            //step 10 Excelファイルに書き込み(まだテスト版）
#if ONLY_GIESTA
            result = writeExcel3(dt); //ジエスタログ解析
#else
            result = writeExcel1(dt);
            result = writeExcel2(dt);
            result = writeExcel3(dt); //ジエスタログ解析
#endif
            
            result = true;
            return result;
        }
        /// <summary>
        /// ワークシート作成用のデリゲードメソッド
        /// </summary>
        delegate void createWorkSheet();

        /// <summary>
        /// コンテンツリスト取得用のデリゲードメソッド 20130326 追加
        /// </summary>
        /// <returns></returns>
        delegate List<string> getContentsList();

        /// <summary>
        /// Excelのメインメソッド
        /// </summary>
        /// <param name="inpuPath">入力するCSVファイル名</param>
        /// <param name="ce">Excel処理クラスファイル</param>
        /// <param name="createMethod">ワークシート作成のメソッド（デリゲードインスタンス）</param>
        /// <returns></returns>
        private bool writeExcel(string inpuPath, ClassExcel ce, createWorkSheet createMethod,DateTime dt,getContentsList contentsList)
        {
            try
            {
                ce.openXLSFile(inpuPath);        ///open excel file TODO                

                //コンテンツ名の一覧を取得する
                //ce.contentsNameList(this.wal.getContentsList(), wal.getSetBaseCategoryNameList());                      ///create contents name list (for pivot table/graph) TODO
                ce.contentsNameList(contentsList(), wal.getSetBaseCategoryNameList());                      ///create contents name list (for pivot table/graph) TODO

                //ピポット＋グラフのワークシートを作成する
                createMethod();
                ce.saveWorkBook(new System.IO.FileInfo(getOutputExcelFileName(inpuPath,dt)));
            }
            catch (Exception ex)
            {
                MessageBox.Show("エラー発生:" + ex.Message + " \nstack=" + ex.StackTrace, "テストメソッドのエラー発生！！", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
                //Console.WriteLine(ex.Message);
                ///あとでエラー発生時の処理をきれいにしておく
            }
            finally
            {
                ce.closeApplication();
            }

            return true;
        }
        /// <summary>
        /// 出力するExcelファイル名を取得する内部メソッド
        /// </summary>
        /// <param name="fullPathName"></param>
        /// <returns></returns>
        private string getOutputExcelFileName(string fullPathName,DateTime dt)
        {
            return Path.Combine(Path.GetDirectoryName(fullPathName),
                         Path.GetFileNameWithoutExtension(fullPathName) + "_" +
                         dt.Year.ToString() + dt.Month.ToString() + dt.Day.ToString() + dt.Hour.ToString() +
                         dt.Minute.ToString() + dt.Second.ToString() + ".xls");
        }
        /// <summary>
        /// Excelファイル１ 滞在時間分布（カテゴリー）の作成
        /// </summary>
        /// <param name="dt">Excelファイル書き込み用の現在の日時</param>
        /// <returns></returns>
        private bool writeExcel1(DateTime dt)
        {
            ClassExcel ce = new ClassExcel();
            createWorkSheet method = new createWorkSheet(ce.createWorksheet);
            getContentsList getList = new getContentsList(this.wal.getOntimeCategoryContensList);
            return writeExcel(this.log1.outputFileName, ce, method, dt,getList);
        }
        /// <summary>
        /// Excelファイル２（平均滞在時間）
        /// </summary>
        /// <param name="dt">Excelファイル書き込み用の現在の日時</param>
        /// <returns></returns>
        private bool writeExcel2(DateTime dt)
        {
            ClassExcel ce = new ClassExcel();
            createWorkSheet method = new createWorkSheet(ce.createWorksheet2);
            getContentsList getList = new getContentsList(this.wal.getAvgContentsList);
            return writeExcel(this.log2.outputFileName, ce, method, dt, getList);
        }
        /// <summary>
        /// Excelファイル３（ジエスタログ解析）
        /// </summary>
        /// <param name="dt">Excelファイル書き込み用の現在の日時</param>
        /// <returns></returns>
        private bool writeExcel3(DateTime dt)
        {
            ClassExcel ce = new ClassExcel();
            createWorkSheet method = new createWorkSheet(ce.createWorksheet3);
            getContentsList getList = new getContentsList(this.wal.getGeiestaDataSumNameList);
            return writeExcel(this.log4.outputFileName, ce, method, dt, getList);
        }



        /// <summary>
        /// 行データの置換メソッド
        /// </summary>
        /// <param name="rows"></param>
        /// <returns></returns>
        private string convert(string[] rows)
        {
            string[] results = new string[rows.Length];
            //CSVのフォーマットでカラムとして、変換する
            for ( int i = 0 ; i < rows.Length ; i++){
                Dictionary<Enum, Object> options = new Dictionary<Enum, Object>();
                options.Add(ConvertMethodsConsts.options.NOW_COLUMN, i);
                results[i] = convertColumn(rows[i],options);
            }
            return string.Join(",", results);
        }
        /// <summary>
        /// カラムデータを変換する。
        /// </summary>
        /// <param name="column"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        private string convertColumn(string column, Dictionary<Enum, Object> options)
        {
            string result = column;
            foreach( ConvertColumn c in this.columnConvert){
                result = c.convert(result,options);
            }
            return result;
        }
        /// <summary>
        /// 入力データ処理を行うパーサーの初期化メソッド
        /// </summary>
        /// <param name="parser"></param>
        /// <param name="checkParser"></param>
        private void InitializeParser(TextFieldParser parser, TextFieldParser checkParser)
        {
            parser.TextFieldType = FieldType.Delimited;
            parser.SetDelimiters(","); // 区切り文字はコンマ
            //とりあえずパース後の前後のTrimは一旦行わない形にする
            //parser.TrimWhiteSpace = false;
            //20120730 shoplist.csvにあわせてTrimする。
            parser.TrimWhiteSpace = true;
            /////////copyParser(parser, checkParser);
        }
        /// <summary>
        /// 入力データを行うパサーの設定値をコピーする。
        /// </summary>
        /// <param name="parser"></param>
        /// <param name="checkParser"></param>
        private void copyParser(TextFieldParser parser, TextFieldParser checkParser)
        {
            checkParser.TextFieldType = parser.TextFieldType;
            checkParser.SetDelimiters(parser.Delimiters); // 区切り文字はコンマ
            //とりあえずパース後の前後のTrimは一旦行わない形にする
            checkParser.TrimWhiteSpace = parser.TrimWhiteSpace;
        }
        //ここから下はプログレスバーの記述
        private ProgressBar _bar = null;
        private long _maxCnt = 0;
        private long _displayCnt = 0;
        private long _addCnt = 0;
        private long _nowCnt = 0;
        private int _barNowCnt = 0;
        private static int _barAddCnt = 10;//5
        private static int _barDivCnt = 10;
        private static int _barInitCnt = 20;
        private int _barMax = _barAddCnt * _barDivCnt + _barInitCnt;
        /// <summary>
        /// プログレスバーを初期化する
        /// </summary>
        /// <param name="bar"></param>
        /// <param name="parser"></param>
        private void initBar(ProgressBar bar, TextFieldParser parser)
        {
            if (bar != null)
            {
                //まずプログレスバーを初期化
                _bar = bar;
                _bar.Minimum = 0;
                _bar.Maximum = _barMax;
                _bar.Value = _barInitCnt;
                _bar.Update();
                Application.DoEvents();

                _barNowCnt = _barInitCnt;

                //データをから読みして、カウンタを取得
                long cnt = 0;
                while (!parser.EndOfData)
                {
                    string[] rows = parser.ReadFields(); // 1行読み込み
                    cnt++;
                }
                parser.Close();

                //アップデートする行番号を取得
                _maxCnt = cnt;
                _displayCnt = cnt / _barDivCnt;//20
                _addCnt = _displayCnt;
            }
        }
        /// <summary>
        /// プログレスバーを描画
        /// </summary>
        private void doBar()
        {
            if (_bar != null)
            {
                if (_nowCnt > _displayCnt)
                {
                    _bar.Value = _barNowCnt + _barAddCnt;
                    _displayCnt += _addCnt;
                    _barNowCnt += _barAddCnt;
                    Application.DoEvents();
                }
                _nowCnt++;
            }
        }
        /// <summary>
        /// プログレスバーを完了（MAX）状態にする。
        /// </summary>
        private void closeBar()
        {
            _bar.Value = _bar.Maximum;
            _bar = null;
        }

        /// <summary>
        /// 入力ファイルの拡張子を返却する
        /// </summary>
        /// <returns></returns>
        public string getInputExt()
        {
            return "csv";
        }
        /// <summary>
        /// 出力ファイルの拡張子を返却する
        /// </summary>
        /// <returns></returns>
        public string getOutputExt()
        {
            return "csv";
        }

    }
}
